﻿using Abp.Collections.Extensions;
using Abp.Dependency;
using Abp.Extensions;
using Abp.UI;
using Mt.Site.Core.Base.Localization;
using Mt.Site.Core.Base.Utils.FileIO.Encode;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using ToolGood.Words;

#pragma warning disable 0618

namespace Mt.Site.Core.BadWordsFilters
{
    public class GroupWords
    {
        public HashSet<string> Words = new HashSet<string>();

        public Dictionary<string, int> IndexDic = new Dictionary<string, int>();

        public int Count;
    }
    /// <summary>
    /// 敏感词管理
    /// </summary>
    public class BadWordFilterManager: IBadWordFilterManager, ISingletonDependency
    {
        private MyLocalizationHelper _LocalHelper
            = new MyLocalizationHelper(MtSiteConsts.LocalizationSourceName);

        private IllegalWordsSearch illegalWordsSearch;

        private IllegalWordsSearch illegalWordsSearch2;

        private readonly IAppFolders _appFolders;

        private bool HasLoadWords = false;

        private List<GroupWords> Co_KeyWords = new List<GroupWords>();
        private readonly IEncodeHelper _encodeHelper;

        private string PinYinTip = "[py]";
        private string FanTiTip = "[ft]";

        public BadWordFilterManager(IAppFolders appFolders, IEncodeHelper encodeHelper)
        {
            _appFolders = appFolders;

            illegalWordsSearch = new IllegalWordsSearch();
            illegalWordsSearch2 = new IllegalWordsSearch();

            _encodeHelper = encodeHelper;
        }

        public string CheckBadWords(string txt, bool throwError = false)
        {
            txt = ContentReplace(WebUtility.HtmlDecode(txt));

            LoadWords();

            var iwsall = illegalWordsSearch.FindAll(txt);
            if (iwsall.Count > 0)
            {
                string badKeyString = iwsall.Select(c=>c.Keyword).JoinAsString("，");
                if (throwError)
                {
                    throw new UserFriendlyException(_LocalHelper.GetString("BadWordErrorMsg", badKeyString));
                }
                return _LocalHelper.GetString("BadWordErrorMsg", badKeyString);
            }
            else if(Co_KeyWords.Count > 0)
            {
                foreach (var item in Co_KeyWords)
                {
                    illegalWordsSearch2.SetKeywords(item.Words);
                    var iwsall2 = illegalWordsSearch2.FindAll(txt);
                    if (iwsall2.Count > 0)
                    {
                        var temHash = new HashSet<int>();
                        foreach (var matched in iwsall2)
                        {
                            temHash.Add(item.IndexDic[matched.MatchKeyword]);
                        }
                        if (temHash.Count == item.Count)
                        {
                            string badKeyString = iwsall2.Select(c => c.Keyword).JoinAsString("，");
                            if (throwError)
                            {
                                throw new UserFriendlyException(_LocalHelper.GetString("BadWordErrorMsg", badKeyString));
                            }
                            return _LocalHelper.GetString("BadWordErrorMsg", badKeyString);
                        }
                    }
                }
            }
            return "";
        }



        public Task<string> GetWordsTextAsync()
        {
            return _encodeHelper.GetStringAsync(GetBadWordFilterPath(), Encoding.UTF8);
            //return File.ReadAllTextAsync(GetBadWordFilterPath());
        }


        public async Task SaveNewWordsText(string txt)
        {
            await _encodeHelper.SaveAsync(GetBadWordFilterPath(), txt, Encoding.UTF8);
            HasLoadWords = false;
            await LoadWordsAsync();
        }


        private string GetBadWordFilterPath()
        {
            var path = Path.Combine(_appFolders.Configs, MtSiteConsts2.Values.BadWordName);

            if (!File.Exists(path))
            {
                File.Create(path).Close();
            }
            return path;
        }

        private string ContentReplace(string htmlStr)
        {
            if (string.IsNullOrEmpty(htmlStr))
            {
                return "";
            }


            //string regEx_style = "<style[^>]*?>[\\s\\S]*?<\\/style>"; //定义style的正则表达式 
            //string regEx_script = "<script[^>]*?>[\\s\\S]*?<\\/script>"; //定义script的正则表达式   

            string regEx_html = "<[^>]+>"; //定义HTML标签的正则表达式   

            //htmlStr = Regex.Replace(htmlStr, regEx_style, "");//删除css
            //htmlStr = Regex.Replace(htmlStr, regEx_script, "");//删除js  不能删js，js可能通过document.write直接向页面上输出内容

            htmlStr = Regex.Replace(htmlStr, regEx_html, "");//删除html标记
            htmlStr = Regex.Replace(htmlStr, "\\s*|\t|\r|\n", "");//去除tab、空格、空行

            //htmlStr = htmlStr.Replace(" ", "");
            //htmlStr = htmlStr.Replace("\"", "");//去除异常的引号" " "
            //htmlStr = htmlStr.Replace("\"", "");

            return htmlStr.Trim();
        }

        private static readonly SemaphoreLocker _lockHelper = new SemaphoreLocker();
        private static readonly object _lockHelper2 = new object();

        //从文件读取关键字.
        private async Task LoadWordsAsync()
        {
            if(HasLoadWords == false)
            {
                await _lockHelper.LockAsync(async () => 
                { 
                    if(HasLoadWords == false)
                    {
                        var task = Task.Run(() =>
                        {
                            loadWordsInternal();
                        }).ConfigureAwait(false);

                        await task;
                    }
                });
            }
        }
        private void LoadWords()
        {
            if (HasLoadWords == false)
            {
                lock(_lockHelper2)
                {
                    if (HasLoadWords == false)
                    {
                        loadWordsInternal();
                    }
                }
            }
        }

        private void loadWordsInternal()
        {
            var tempSet = new HashSet<string>();
            var temp_Co_KeyWords = new List<GroupWords>();

            var lines = _encodeHelper.GetString(GetBadWordFilterPath(), Encoding.UTF8)
                .Split("\r\n".ToCharArray(), StringSplitOptions.None);

            foreach (var key in lines)
            {
                if (!key.IsNullOrEmpty())
                {
                    // 如果是判断稿件中同时存在多个关键词才算敏感的话， 这几个关键词列在同一行，用|分隔
                    var subKeys = key.Trim().Split("|", StringSplitOptions.RemoveEmptyEntries);

                    if (subKeys.Length == 1)
                    {
                        TryAddWords(key, tempSet, null);
                    }
                    else
                    {
                        var itemData = new GroupWords();
                        itemData.Count = subKeys.Length;
                        int i = 0;
                        foreach (var subKeyItem in subKeys)
                        {
                            TryAddWords(subKeyItem, itemData.Words, (c) => itemData.IndexDic.Add(c, i));
                            i++;
                        }
                        temp_Co_KeyWords.Add(itemData);
                    }
                }
            }

            illegalWordsSearch.SetKeywords(tempSet);
            Co_KeyWords = temp_Co_KeyWords;
        }

        private void TryAddWords(string originKey, HashSet<string> allKeys, Action<string> action)
        {
            bool getFanTi = originKey.IndexOf(FanTiTip) >= 0;
            bool getPinYin = originKey.IndexOf(PinYinTip) >= 0;

            string key;
            if(getFanTi)
            {
                if (getPinYin)
                {
                    key = originKey.Replace(FanTiTip, "").Replace(PinYinTip, "");
                }
                else 
                {
                    key = originKey.Replace(FanTiTip, "");
                }
            }
            else
            {
                if (getPinYin)
                {
                    key = originKey.Replace(PinYinTip, "");
                }
                else
                {
                    key = originKey;
                }
            }

            if (getFanTi)
            {
                var tempWord = WordsHelper.ToTraditionalChinese(key);
                if (allKeys.Add(tempWord))
                {
                    if (action != null)
                    {
                        action(tempWord);
                    }
                }
            }

            if (getPinYin)
            {
                int n = key.Length;
                int[] book = new int[n + 1];
                dfs(0, n, book, b =>
                {
                    StringBuilder sb = new StringBuilder();

                    for (int i = 0; i < key.Length; i++)
                    {
                        var @char = key[i];
                        if (b[i] == 1
                            && ((@char <= 0x9FA5 && @char >= 0x4E00) || (@char >= 0x20000 && @char <= 0x2B81D)))
                        {
                            sb.Append(WordsHelper.GetPinyin(@char.ToString()).ToLower());
                        }
                        else
                        {
                            sb.Append(@char);
                        }
                    }
                    var tempWord = sb.ToString();
                    if (allKeys.Add(tempWord))
                    {
                        if (action != null)
                        {
                            action(tempWord);
                        }
                    }
                });
            }
            else
            {
                if (allKeys.Add(key))
                {
                    if (action != null)
                    {
                        action(key);
                    }
                }
            }
        }

        //做拼音与简体（？）的全排列
        private static void dfs(int step, int n, int[] book, Action<int[]> action)
        {
            if (step == n + 1)
            {
                action(book);
                return; 
            }
            book[step] = 1; 
            dfs(step + 1, n, book, action);
            book[step] = 0; 
            dfs(step + 1, n, book, action);
        }
    }
}
